<template>
  <div class="main-content-box">
    <div class="demoDiv">
      <div class="title_for_layout">
        <h3>高亮相邻节点，随机节点颜色</h3>
        <h3>鼠标hover到节点上查看效果，边和节点的tooltip</h3>
      </div>
      <div class="toolDiv">
        <div class="zoom zoomOut" title="放大" @click="getZoomOut">
          <i class="iconfont">&#xe640;</i>
        </div>
        <div class="zoom zoomIn" title="缩小" @click="getZoomIn">
          <i class="iconfont">&#xe624;</i>
        </div>
        <div class="zoom refreshViews" title="刷新拓扑图" @click="refreshViews">
          <i class="iconfont">&#xe64d;</i>
        </div>
      </div>
      <div id="container"></div>
    </div>
  </div>
</template>

<script>
import G6 from '@antv/g6';
import Util from "@antv/g6/lib/util";
import { getHighlight } from "../../../nodeApi/api/getData.js"
export default {
  name: 'g6Highlight',
  data () {
    return {
      orgData: {},
      colors: [
        '#BDD2FD',
        '#BDEFDB',
        '#C2C8D5',
        '#FBE5A2',
        '#F6C3B7',
        '#B6E3F5',
        '#D3C6EA',
        '#FFD8B8',
        '#AAD8D8',
        '#FFD6E7',
      ],
      strokes: [
        '#5B8FF9',
        '#5AD8A6',
        '#5D7092',
        '#F6BD16',
        '#E8684A',
        '#6DC8EC',
        '#9270CA',
        '#FF9D4D',
        '#269A99',
        '#FF99C3',
      ],
      currentGraph: {}
    }
  },
  mounted () {
    getHighlight().then(res => {
      this.orgData = res
      /**
       * 给节点设置随机的颜色
       */
      this.orgData.nodes.forEach((item, index) => {
        if (!item.style) item.style = {};
        item.style.fill = this.colors[index % this.colors.length]
        item.style.stroke = this.strokes[index % this.strokes.length]
      })
      setTimeout(() => {
        this.initGraph()
      }, 10);
    })
  },
  methods: {
    initGraph () {
      /**
       * 高亮相邻节点的demo中使用节点动画
       * 新增了节点动画，但在defaultNode中设置type后，并未生效
       * 考虑通过状态设置的方式实现节点的动画效果 --- 此方法不生效
       */
      G6.registerNode(
        'circle-animate',
        {
          setState (name, value, item) {
            // 获取该节点上的第一个图形
            const shape = group.get('keyShape');
            console.log(shape)
            if (name === 'animate') {
              if (value) {
                shape.animate(
                  ratio => {
                    // 每一帧的操作，入参 ratio：这一帧的比例值（Number）。返回值：这一帧需要变化的参数集（Object）。
                    // 先变大、再变小
                    const diff = ratio <= 0.5 ? ratio * 10 : (1 - ratio) * 10;
                    let radius = cfg.size;
                    if (isNaN(radius)) radius = radius[0];
                    // 返回这一帧需要变化的参数集，这里只包含了半径
                    return {
                      r: radius / 2 + diff,
                    };
                  },
                  {
                    // 动画重复
                    repeat: true,
                    duration: 3000,
                    easing: 'easeCubic',
                  },
                );
              } else {
                shape.stopAnimate()
              }
            }
          },
        },
        'circle',
      ); // 该自定义节点继承了内置节点 'circle'，除了被复写的 afterDraw 方法外，其他按照 'circle' 里的函数执行。

      /**
       * 设置节点的动画效果 -- 未生效
       */
      const dashArray = [
        [0, 1],
        [0, 2],
        [1, 2],
        [0, 1, 1, 2],
        [0, 2, 1, 2],
        [1, 2, 1, 2],
        [2, 2, 1, 2],
        [3, 2, 1, 2],
        [4, 2, 1, 2],
      ];
      const lineDash = [4, 2, 1, 2];
      const interval = 9;
      G6.registerEdge(
        'can-running',
        {
          setState (name, value, item) {
            const shape = item.get('keyShape');
            if (name === 'running') {
              if (value) {
                const length = shape.getTotalLength(); // 后续 G 增加 totalLength 的接口
                let totalArray = [];
                for (let i = 0; i < length; i += interval) {
                  totalArray = totalArray.concat(lineDash);
                }
                let index = 0;
                shape.animate(
                  () => {
                    const cfg = {
                      lineDash: dashArray[index].concat(totalArray),
                    };
                    index = (index + 1) % interval;
                    return cfg;
                  },
                  {
                    repeat: true,
                    duration: 3000,
                  },
                );
              } else {
                shape.stopAnimate();
                // shape.attr('lineDash', null);
              }
            }
          },
        },
        'cubic-horizontal',
      );


      const width = document.getElementById('container').scrollWidth;
      const height = document.getElementById('container').scrollHeight || 650;
      const graph = new G6.Graph({
        container: 'container',
        width,
        height,
        layout: {
          type: 'force',
          preventOverlap: true,
          edgeStrength: 0.7,
          nodeStrength: -100, // 节点的斥力，正值为引力
          // workerEnabled: true,  //暂时屏蔽此属性
        },
        modes: {
          default: [
            'drag-canvas',
            {
              type: 'tooltip',
              formatText: function formatText (model) {
                return model.name;
              },
              offset: 30
            },
            {
              type: 'edge-tooltip',
              formatText: function formatText (model, e) {
                const edge = e.item;
                return (
                  '来源：' +
                  edge.getSource().getModel().name +
                  '<br/>去向：' +
                  edge.getTarget().getModel().name
                );
              },
              offset: 30
            },
            // 'activate-relations' // 此属性为高亮相邻节点，本例为自定义高亮，故注释
          ],
        },
        defaultNode: {
          size: [20, 20],
          type: 'circle-animate',
          style: {
            lineWidth: 2,
            fill: '#DEE9FF',
            stroke: '#5B8FF9',
          },
        },
        defaultEdge: {
          // type: 'can-running',
          size: 1,
          style: {
            stroke: '#e2e2e2',
            lineAppendWidth: 2,
          },
        },
        nodeStateStyles: {
          highlight: {
            opacity: 1,
          },
          dark: {
            opacity: 0.2,
          },
        },
        edgeStateStyles: {
          highlight: {
            lineWidth: 2,
            stroke: 'cyan',
            lineDash: [2, 2, 2, 2],
            // animate: true,  // 此三个属性不生效，？设置有问题
            // repeat: true,
            // duration: 3000
          },
        },
      });

      function clearAllStats () {
        graph.setAutoPaint(false);
        graph.getNodes().forEach(function (node) {
          graph.clearItemStates(node);
        });
        graph.getEdges().forEach(function (edge) {
          graph.clearItemStates(edge);
        });
        graph.paint();
        graph.setAutoPaint(true);
      }

      graph.on('node:mouseenter', function (e) {
        const item = e.item;
        graph.setAutoPaint(false);
        graph.getNodes().forEach(function (node) {
          graph.clearItemStates(node);
          graph.setItemState(node, 'dark', true);
          // graph.setItemState(node, 'animate', true);
        });
        graph.setItemState(item, 'dark', false);
        graph.setItemState(item, 'highlight', true);
        graph.getEdges().forEach(function (edge) {
          if (edge.getSource() === item) {
            graph.setItemState(edge.getTarget(), 'dark', false);
            graph.setItemState(edge.getTarget(), 'highlight', true);
            graph.setItemState(edge, 'highlight', true);
            // graph.setItemState(edge, 'running', true);

            /**
             * toFront()方法
             * 将元素的层级设置到最上层，即当有元素重叠时，将元素置于顶层。
             */

            edge.toFront();
          } else if (edge.getTarget() === item) {
            graph.setItemState(edge.getSource(), 'dark', false);
            graph.setItemState(edge.getSource(), 'highlight', true);
            graph.setItemState(edge, 'highlight', true);
            // graph.setItemState(edge, 'running', true);
            edge.toFront();
          } else {
            graph.setItemState(edge, 'highlight', false);
            // graph.setItemState(edge, 'running', false);
          }
        });
        graph.paint();
        graph.setAutoPaint(true);
      });
      graph.on('node:mouseleave', clearAllStats);
      graph.on('canvas:click', clearAllStats);

      graph.data({
        nodes: this.orgData.nodes,
        edges: this.orgData.edges.map(function (edge, i) {
          edge.id = 'edge' + i;
          return Object.assign({}, edge);
        }),
      });

      graph.render();

      this.currentGraph = graph
    },
    /**
     * 拓扑图的缩小、放大
     */
    getZoomOut () {
      let currentZoom = this.currentGraph.getZoom();
      this.currentGraph.zoomTo(currentZoom + 0.1, this.getViewCenter());
    },
    getZoomIn () {
      const currentZoom = this.currentGraph.getZoom();
      this.currentGraph.zoomTo(currentZoom - 0.1, this.getViewCenter());
    },
    getFormatPadding () {
      return Util.formatPadding(this.currentGraph.get("fitViewPadding"));
    },
    getViewCenter () {
      const padding = this.getFormatPadding();
      // console.log(padding)
      const graph = this.currentGraph;
      const width = this.currentGraph.get("width");
      const height = graph.get("height");
      return {
        x: (width - padding[2] - padding[3]) / 2 + padding[3],
        y: (height - padding[0] - padding[2]) / 2 + padding[0],
      };
    },
    /**
     * 刷新拓扑图
     */
    refreshViews () {
      this.currentGraph.clear()
      this.currentGraph.destroy();
      setTimeout(() => {
        this.initGraph()
      }, 0);
    },
  }
}
</script>

<style scoped>
.main-content-box {
  width: 95%;
  margin: 5px 10px;
}
.demoDiv {
  position: relative;
  padding: 20px;
  /* border: 1px solid skyblue; */
  /* min-height: 600px; */
}
.toolDiv {
  width: 50px;
  border: 1px solid #f5f5f5;
  position: absolute;
  z-index: 100;
}
.title_for_layout {
  position: absolute;
  top: 0;
  left: 100px;
  color: crimson;
}
.zoom {
  cursor: pointer;
  border: 1px solid #f5f5f5;
  font-size: 26px;
  background-color: #ffffff;
}
.demoDiv >>> .g6-tooltip {
  border: 1px solid #e2e2e2;
  border-radius: 4px;
  font-size: 12px;
  color: #000;
  background-color: rgba(255, 255, 255, 0.9);
  padding: 10px 8px;
  box-shadow: rgb(174, 174, 174) 0px 0px 10px;
}
</style>